use std::fmt::{Debug, Formatter, Pointer};
use std::ops::Add;

use crate::gen_title;

pub fn ch2_17_1() {
    gen_title("ch2_17_1", 18);

    fn add_i8(a: i8, b: i8) -> i8 {
        a + b
    }

    fn add_i32(a: i32, b: i32) -> i32 {
        a + b
    }

    fn add_f64(a: f64, b: f64) -> f64 {
        a + b
    }

    println!("{}", add_i8(1, 2));
    println!("{}", add_i32(1, 2));
    println!("{}", add_f64(1.0, 2.0));
}

pub fn ch2_17_2() {
    gen_title("ch2_17_2", 18);

    fn add<T: std::ops::Add<Output = T>>(a: T, b: T) -> T {
        a + b
    }

    println!("{}", add(1, 2));
    println!("{}", add(1.0, 2.0));
}

pub fn ch2_17_3() {
    gen_title("ch2_17_3", 18);

    fn largest<T: PartialOrd>(list: &[T]) -> &T {
        let mut largest = &list[0];

        for item in list.iter() {
            if item > largest {
                largest = item;
            }
        }

        largest
    }

    let number_list = vec![34, 50, 25, 100, 64];

    let result = largest(&number_list);
    println!("The largest number is {}", result);

    let char_list = vec!['y', 'm', 'a', 'q'];

    let result = largest(&char_list);
    println!("The largest char is {}", result);
}

pub fn ch2_17_4() {
    gen_title("ch2_17_4", 18);

    #[derive(Debug)]
    struct Point<T> {
        x: T,
        y: T,
    }

    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };

    println!("integer: {:?}", integer);
    println!("float: {:?}", float);
}

pub fn ch2_17_5() {
    gen_title("ch2_17_5", 18);

    #[derive(Debug)]
    struct Point<T, U> {
        x: T,
        y: U,
    }

    let p = Point { x: 1, y: 1.1 };
    println!("p: {:?}", p);
}

pub fn ch2_17_6() {
    gen_title("ch2_17_6", 18);

    struct Point<T> {
        x: T,
        y: T,
    }

    impl<T> Point<T> {
        fn x(&self) -> &T {
            &self.x
        }
    }

    let p = Point { x: 5, y: 10 };
    println!("p.x = {}", p.x());
}

pub fn ch2_17_7() {
    gen_title("ch2_17_7", 18);

    struct Point<T, U> {
        x: T,
        y: U,
    }

    impl<T, U> Point<T, U> {
        fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
            Point {
                x: self.x,
                y: other.y,
            }
        }
    }

    let p1 = Point { x: 5, y: 10.4 };
    let p2 = Point { x: "Hello", y: 'c' };

    let p3 = p1.mixup(p2);
    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

pub fn ch2_17_8() {
    gen_title("ch2_17_8", 18);

    fn display_array(arr: &[i32]) {
        println!("{:?}", arr);
    }

    let arr: [i32; 3] = [1, 2, 3];
    display_array(&arr);

    let arr: [i32; 2] = [1, 2];
    display_array(&arr);
}

pub fn ch2_17_9() {
    gen_title("ch2_17_9", 18);

    fn display_array<T: std::fmt::Debug>(arr: &[T]) {
        println!("{:?}", arr);
    }

    let arr: [i32; 3] = [1, 2, 3];
    display_array(&arr);

    let arr: [f64; 2] = [1.0, 2.0];
    display_array(&arr);
}

pub fn ch2_17_10() {
    gen_title("ch2_17_10", 18);

    fn display_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {
        println!("{:?}", arr);
    }

    let arr: [i32; 3] = [1, 2, 3];
    display_array(arr);

    let arr: [i32; 2] = [1, 2];
    display_array(arr);
}

pub fn ch2_17_11() {
    gen_title("ch2_17_11", 18);

    fn something<T: Debug>(val: T)
    where
        Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue,
    {
        println!("{:?}", val);
    }

    pub enum Assert<const CHECK: bool> {}

    pub trait IsTrue {}

    impl IsTrue for Assert<true> {}

    something([0u8; 0]);
    something([0u8; 512]);
    // something([0u8; 1024]);
}

pub fn ch2_17_pra_1() {
    gen_title("ch2_17_pra_1", 18);

    #[derive(Debug)]
    struct A;
    #[derive(Debug)]
    struct S(A);
    #[derive(Debug)]
    struct SGen<T: Debug>(T);

    fn reg_fn(_s: S) {
        println!("_s: {:?}", _s);
    }

    fn gen_spec_t(_s: SGen<A>) {
        println!("_s: {:?}", _s);
    }

    fn gen_spec_i32(_s: SGen<i32>) {
        println!("_s: {:?}", _s);
    }

    fn generic<T: Debug>(_s: SGen<T>) {
        println!("_s: {:?}", _s);
    }

    reg_fn(S(A));
    gen_spec_t(SGen(A));
    gen_spec_i32(SGen(22));

    generic::<char>(SGen('a'));
    generic(SGen('b'));
}

pub fn ch2_17_pra_2() {
    gen_title("ch2_17_pra_2", 18);

    fn sum<T: Add<Output = T>>(a: T, b: T) -> T {
        a + b
    }

    println!("{}", sum(1, 2));
    println!("{}", sum(1.0, 2.0));
}

pub fn ch2_17_pra_3() {
    gen_title("ch2_17_pra_3", 18);

    #[derive(Debug)]
    struct Point<T> {
        x: T,
        y: T,
    }

    let integer = Point { x: 5, y: 10 };
    let float = Point { x: 1.0, y: 4.0 };

    println!("integer: {:?}", integer);
    println!("float: {:?}", float);
}

pub fn ch2_17_pra_4() {
    gen_title("ch2_17_pra_4", 18);

    struct Val<T> {
        val: T,
    }

    impl<T> Val<T> {
        fn value(&self) -> &T {
            &self.val
        }
    }

    let x = Val { val: 3.0 };
    let y = Val {
        val: "hello".to_string(),
    };
    println!("x.value() = {}", x.value());
    println!("y.value() = {}", y.value());
}

pub fn ch2_17_pra_5() {
    gen_title("ch2_17_pra_5", 18);

    struct Point<T, U> {
        x: T,
        y: U,
    }

    impl<T, U> Point<T, U> {
        fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {
            Point {
                x: self.x,
                y: other.y,
            }
        }
    }

    let p1 = Point { x: 5, y: 10 };
    let p2 = Point {
        x: "Hello",
        y: "中",
    };

    let p3 = p1.mixup(p2);

    println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

pub fn ch2_17_pra_6() {
    gen_title("ch2_17_pra_6", 18);

    struct Point<T> {
        x: T,
        y: T,
    }

    impl Point<f32> {
        fn distance_from_origin(&self) -> f32 {
            (self.x.powi(2) + self.y.powi(2)).sqrt()
        }
    }

    let p = Point {
        x: 5_f32,
        y: 10_f32,
    };
    println!("p.distance_from_origin() = {}", p.distance_from_origin());
}

pub fn ch2_17_pra_7() {
    gen_title("ch2_17_pra_7", 18);

    struct ArrayPair<T, const N: usize> {
        left: [T; N],
        right: [T; N],
    }

    impl<T: Debug, const N: usize> Debug for ArrayPair<T, N> {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            f.write_str(
                format!(
                    "ArrayPair {{ left: {:?}, right: {:?} }}",
                    self.left, self.right
                )
                .as_str(),
            )
        }
    }

    let pair = ArrayPair {
        left: [1, 2, 3],
        right: [4, 5, 6],
    };

    println!("{:?}", pair);
}

pub fn ch2_17_pra_8() {
    gen_title("ch2_17_pra_8", 18);

    fn foo<const N: usize>() {
        println!("N = {}", N);
    }

    fn bar<T, const M: usize>() {
        foo::<M>();

        foo::<2021>();

        foo::<{ 20 * 100 + 20 * 10 + 1 }>();

        let x: [u8; M] = [11; M];

        println!("x = {:?}", x);
    }

    bar::<u64, 20>();
}

pub fn ch2_17_pra_9() {
    gen_title("ch2_17_pra_9", 18);

    struct MinSlice<T, const N: usize> {
        pub head: [T; N],
        pub tail: Vec<T>,
    }
    impl<T: Clone, const N: usize> MinSlice<T, N>
    where
        [T; N]: for<'a> TryFrom<&'a [T]>,
    {
        pub fn from_slice(slice: &[T]) -> Option<MinSlice<T, N>> {
            if slice.len() < N {
                return None;
            }
            let (head, tail) = slice.split_at(N);
            // 直接克隆到 Vec<T>
            match head.try_into() {
                Ok(head_array) => Some(MinSlice {
                    head: head_array,
                    tail: tail.to_vec(),
                }),
                Err(_) => None,
            }
        }
    }

    let slice: &[u8] = b"Hello, world";
    let reference: Option<&u8> = slice.get(6);
    assert!(reference.is_some());

    let slice: &[u8] = b"Hello, world";
    let min_slice = MinSlice::<u8, 5>::from_slice(slice).unwrap();
    let value: u8 = min_slice.tail[1];
    assert_eq!(value, b' ');
    println!("value = {}", value);
}

pub fn ch2_17_pra_10() {
    gen_title("ch2_17_pra_10", 18);

    #[derive(Debug)]
    struct Array<T, const N: usize> {
        data: [T; N],
    }

    let arrays = [
        Array { data: [1, 2, 3] },
        Array { data: [1, 2, 3] },
        Array { data: [1, 2, 4] },
    ];

    println!("arrays = {:?}", arrays);
}

pub fn ch2_17_pra_11() {
    gen_title("ch2_17_pra_11", 18);

    fn print_array<T: Debug>(arr: T) {
        println!("arr: {:?}", arr);
    }

    let arr = [1, 2, 3];
    print_array(arr);

    let arr = ["hello", "world"];
    print_array(arr);
}

pub fn ch2_17_pra_12() {
    gen_title("ch2_17_pra_12", 18);

    fn check_size<T>(val: T)
    where
        Assert<{ core::mem::size_of::<T>() < 768 }>: IsTrue,
        T: Debug,
    {
        println!("val: {:?}", val);
    }

    check_size([0u8; 767]);
    check_size([0i32; 191]);
    check_size(["hello你好"; 47]);
    check_size([(); 31].map(|_| "hello你好".to_string()));
    check_size(['中'; 191]);

    pub enum Assert<const CHECK: bool> {}

    pub trait IsTrue {}
    impl IsTrue for Assert<true> {}
}
